package com.socialin.android.hackaton.draw.drawing;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.WindowManager;

import com.socialin.android.hackaton.draw.R;
import com.socialin.android.hackaton.draw.listener.DoubleTapListener;
import com.socialin.android.hackaton.draw.listener.JsonConstants;
import com.socialin.android.hackaton.draw.listener.JsonReceiver;
import com.socialin.android.hackaton.draw.listener.JsonSender;
import com.socialin.android.hackaton.draw.util.Util;
import com.socialin.picsin.camera.view.CameraCallBack;
import com.socialin.picsin.camera.view.ShapeDrawer;

public class DrawingView extends View implements OnScaleGestureListener, JsonReceiver,CameraCallBack {

	private static final int EVENT_SEND_DELAY = 200;
	
	private int doubleTapW = 100;
	private int doubleTapH = 100;
	
	private int doubleTapWPx;
	private int doubleTapHPx;
	
	private DoubleTapListener mDoubleListener = null;
	
	private ScaleGestureDetector mGestureDetector = null;
	
	private GestureDetector mDoubleTapGestureListener = null;
	
	public void setDoubleTapListener(DoubleTapListener listener) {
		mDoubleListener = listener;
	}
	
	private float mScaleFactor = 1.0f;
	private float mViewPortX, mViewPortY;
	
	private int mViewW, mViewH;
	
	private Path mFriendDrawingPath = new Path();
	
	private Paint mPaint = null;
	private Paint clearModePaint;
	
	private Bitmap mDrawingBitmap = Bitmap.createBitmap(DEFAULT_SCREEN_W, DEFAULT_SCREEN_H, Config.ARGB_8888);
	
	private Canvas mCanvas = new Canvas(mDrawingBitmap);
	
	private JsonSender mJsonSender = null;
	
	private StringBuilder mSendingStringBuilder = new StringBuilder();
	
	private long mLastSentTime = 0;
	
	private ShapeDrawer1 shapeDrawer1;
	
	private HashMap<String, DrawingController> mControllers = new HashMap<String, DrawingController>();
	
	public static final int DEFAULT_SCREEN_W = 800;
	public static final int DEFAULT_SCREEN_H = 480;
	
	Path path = new Path();
	
	String uuid;
	
	float scaleX, scaleY;
	
	public static final int DRAW_MODE_BRUSH 		= 1;
	public static final int DRAW_MODE_PUT_ICON 	= 2;
	public final static int DRAW_MODE_SHAPE 		= 3;
	
	public static final int ICON_DRAW_PUT_ICON 		= 1;
	public static final int ICON_DRAW_MOVE_ICON 		= 2;
	public static final int ICON_DRAW_REMOVE_ICON		= 3;
	
	private int mCurrentMode = DRAW_MODE_BRUSH;
	
	private Bitmap mIcon = null;
	
	private ArrayList<Item> mItems = new ArrayList<Item>();
	
	private boolean needRedrawAllItems = false;
	
	public static int mDrawableW = 0;
	public static int mDrawableH = 0;
	
	//private ShapeDrawer shapeDrawer;
	
	private boolean isPausedOutSync = false;
	private boolean isAccessingData = true;
	
	private ArrayList<JSONObject> mWaitingList = new ArrayList<JSONObject>();
	
	public DrawingView(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		mGestureDetector = new ScaleGestureDetector(context, this);
		mPaint = new Paint();
		mPaint.setColor(Color.RED);
		mPaint.setStrokeWidth(5);
		mPaint.setStrokeJoin(Join.ROUND);
		mPaint.setStrokeCap(Cap.ROUND);
		mPaint.setStyle(Style.STROKE);
		mPaint.setAntiAlias(true);
		mPaint.setFilterBitmap(true);
		
		clearModePaint = new Paint();
		clearModePaint.setAntiAlias(true);
		clearModePaint.setFilterBitmap(true);
		PorterDuffXfermode xfer = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
		clearModePaint.setXfermode(xfer);
		
		
		uuid = PreferenceManager.getDefaultSharedPreferences(context).getString("uuid", "");
		mControllers.put(uuid, new DrawingController());
		
		mIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
		mDrawableW = mIcon.getWidth();
		mDrawableH = mIcon.getHeight();
		
		
		DisplayMetrics dm = new DisplayMetrics();
		
		((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(dm);
		
		doubleTapWPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, doubleTapW, dm);
		doubleTapHPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, doubleTapH, dm);
		
		mDoubleTapGestureListener = new GestureDetector(context, new SimpleOnGestureListener() {
			@Override
			public boolean onDoubleTap(MotionEvent e) {
				if (mDoubleListener != null)
					if (e.getX() <= doubleTapWPx && e.getY() >= mViewH - doubleTapHPx) {
						mDoubleListener.onDoubleTap();
						return true;
					}
				
				return false;
			}
		});

	}
	
	public void initShapeDrawer(Bitmap shapeSizeHandleBm,Bitmap shapeScaleHandeBitmap)
	{
		shapeDrawer1 = new ShapeDrawer1(shapeSizeHandleBm, shapeScaleHandeBitmap);
	}
	
	public void setJsonSender(JsonSender jsonSender) {
		mJsonSender = jsonSender;
	}
	
	private void addEvent(MotionEvent event) {
		
		int relX = (int) ((event.getX() / (float) mViewW) * DEFAULT_SCREEN_W);
		int relY = (int) ((event.getY() / (float) mViewH) * DEFAULT_SCREEN_H);
		
		int action = event.getAction();
		
		JSONObject json = new JSONObject();
		JSONObject params = new JSONObject();
		try {
			json.put("action", "draw");
			params.put("event", action);
			JSONArray array = new JSONArray();
			array.put(relX);
			array.put(relY);
			params.put("coords", array);
			params.put("type", "path");
			json.put("params", params);
			json.put("uuid", uuid);
		} catch (JSONException e1) {
			e1.printStackTrace();
		}
		
		if (isPausedOutSync)
			mWaitingList.add(json);
		else
			mJsonSender.onSend(json);
	}
	
	private void onCustomTouchEvent(int action, int x, int y, String uuid) {
		DrawingController controller = mControllers.get(uuid);
		if (controller == null)
			mControllers.put(uuid, controller = new DrawingController());
		controller.handleTouch(action, x, y, path);
		mPaint.setColor(controller.getColor());
		mCanvas.drawPath(path, mPaint);
		invalidate();
	}
	
	
	private Item mDraggingItem = null;
	
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		
		final float touchX = event.getX();
		final float touchY = event.getY();
		
		int relX = (int) ((touchX / (float) mViewW) * DEFAULT_SCREEN_W);
		int relY = (int) ((touchY / (float) mViewH) * DEFAULT_SCREEN_H);

		final int action = event.getAction();
		
		mDoubleTapGestureListener.onTouchEvent(event);
		
		switch (mCurrentMode) {
		case DRAW_MODE_PUT_ICON:
			
			JSONObject json = new JSONObject();
			JSONObject params = new JSONObject();
			try {
				json.put("action", "draw");
			} catch (JSONException e2) {
				// TODO Auto-generated catch block
				e2.printStackTrace();
			}
			JSONArray array = new JSONArray();
			array.put(relX);
			array.put(relY);
			
			try {
				params.put("coords", array);
				params.put("type", "icon");
			} catch (JSONException e) {
				e.printStackTrace();
			}
			
			
			try {
				Item item = getTouchedItem(relX, relY);
				switch (action) {
				case MotionEvent.ACTION_DOWN:
					if (item == null) { // touched empty space
						params.put("action", ICON_DRAW_PUT_ICON);
						Item newItem = new Item(relX, relY);
						params.put("item_uuid", newItem.uuid);
						mItems.add(newItem);
						needRedrawAllItems = true;
						
						json.put("params", params);
						json.put("uuid", uuid);
						if (isPausedOutSync) {
							mWaitingList.add(json);
						} else {
							mJsonSender.onSend(json);	
						}
			
					} else {
						mDraggingItem = item;
					}
					
					break;
					
				case MotionEvent.ACTION_MOVE:
					if (mDraggingItem != null) {
						params.put("item_uuid", mDraggingItem.uuid);
						params.put("action", ICON_DRAW_MOVE_ICON);
						
						mDraggingItem.rect.left = relX;
						mDraggingItem.rect.top = relY;
						
						needRedrawAllItems = true;
						
						json.put("params", params);
						json.put("uuid", uuid);
						if (isPausedOutSync) {
							mWaitingList.add(json);
						} else {
							mJsonSender.onSend(json);	
						}
					}
					
					break;
				
				case MotionEvent.ACTION_UP:
					mDraggingItem = null;
					break;
				}
				
				
				invalidate();
				
			} catch (JSONException e1) {
				e1.printStackTrace();
			}
			
			break;
			
		case DRAW_MODE_BRUSH:
			addEvent(event);
			mGestureDetector.onTouchEvent(event);
			
			
			onCustomTouchEvent(action, relX, relY, uuid);
			break;
		case DRAW_MODE_SHAPE:
			
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				shapeDrawer1.touchStart(event.getX(), event.getY());
				break;

			case MotionEvent.ACTION_MOVE:
				shapeDrawer1.touchMove(event.getX(), event.getY());
				break;
			case MotionEvent.ACTION_UP:
				shapeDrawer1.touch_up();
				break;
			}
			invalidate();
			
			break;
		}
		return true;
	}
	
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		
		mViewW = right - left;
		mViewH = bottom - top;
		
		scaleX = (float) mViewW / (float) DEFAULT_SCREEN_W;
		scaleY = (float) mViewH / (float) DEFAULT_SCREEN_H;
		
//		if (mDrawingBitmap != null) {
//			if (!mDrawingBitmap.isRecycled()) {
//				mDrawingBitmap.recycle();
//			}
//		}
//		mCanvas.setBitmap(mDrawingBitmap);
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawColor(Color.WHITE);
//		mCanvas.drawPath(mCurrentDrawingPath, mPaint);
		Collection<DrawingController> controllers = mControllers.values();
//		for (DrawingController controller : controllers) {
//			mPaint.setColor(controller.getColor());
//			mPaint.setColor(Color.RED);
//			mCanvas.drawPath(controller.mCurrentDrawingPath, mPaint);
//		}
		
//		canvas.translate(mViewPortX, mViewPortY);
//		canvas.scale(mScaleFactor, mScaleFactor);
		canvas.save();
		canvas.scale(scaleX, scaleY);
		canvas.drawBitmap(mDrawingBitmap, 0, 0, mPaint);
		
		
		canvas.restore();
		if(mCurrentMode == DRAW_MODE_SHAPE)
		{
			
			canvas.drawRect(shapeDrawer1.getDrawRect(), clearModePaint);
			shapeDrawer1.drawShape(canvas);
		}
		
		canvas.scale(scaleX, scaleY);
		for (Item item : mItems) {
			canvas.drawBitmap(mIcon, item.rect.left, item.rect.top, mPaint);
		}

	}

	@Override
	public boolean onScale(ScaleGestureDetector detector) {
		mScaleFactor *= detector.getScaleFactor();
		
		mViewPortX = detector.getFocusX() - mViewW * mScaleFactor;
		return true;
	}

	@Override
	public boolean onScaleBegin(ScaleGestureDetector detector) {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public void onScaleEnd(ScaleGestureDetector detector) {
		
	}


	@Override
	public void onReceive(JSONObject json) {
		
		if (!isAccessingData)
			return;
		
		try {
			JSONObject params = (JSONObject) json.get(JsonConstants.PARAMS);
			String type = params.getString("type");
			
			System.out.println("type : " + type);
			
			if (type.equals("icon")) {
				JSONArray coords = params.getJSONArray("coords");
				
				int action = params.getInt("action");
				
				String itemUuid = params.getString("item_uuid");
				
				int remoteX = coords.getInt(0);
				int remoteY = coords.getInt(1);
				
				switch (action) {
				case ICON_DRAW_PUT_ICON:
					mItems.add(new Item(remoteX, remoteY, itemUuid));
					break;
					
				case ICON_DRAW_MOVE_ICON:
					System.out.println("ICON_DRAW_MOVE_ICON, uuid : " + itemUuid);
					
					for (Item i : mItems) {
						System.out.println(i.uuid);
					}
					
					Item selectedItem = findItemByUuid(itemUuid);
					selectedItem.rect.left = remoteX;
					selectedItem.rect.top = remoteY;
					break;
				}
				invalidate();
				
			} else if (type.equals("path")) {
				int action = params.getInt(JsonConstants.EVENT);
				JSONArray coords = params.getJSONArray("coords");
				onCustomTouchEvent(action, coords.getInt(0), coords.getInt(1), json.getString("uuid")); //x : 0 - 480
			} else if (type.equals("photo")) {
				JSONArray dstRectArray = params.getJSONArray("dstRect");
				
				Rect dstRect = new Rect(dstRectArray.getInt(0), dstRectArray.getInt(1), dstRectArray.getInt(2), dstRectArray.getInt(3));
				Bitmap bm = Util.getBitmapFromBase64(params.getString("bitmap"));
				
				Rect srcRect = new Rect(0, 0, bm.getWidth(), bm.getHeight());
				mCanvas.drawBitmap(bm, srcRect, dstRect, mPaint);
				
				invalidate();

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private Item getTouchedItem(int x, int y) {
		int itemCount = mItems.size();
		
		Item selecetedItem = null;
		
		for (int i = itemCount - 1; i > -1; --i) {
			Item item = mItems.get(i);
			
			if (x >= item.rect.left && x <= item.rect.right) {
				if (y >= item.rect.top && y <= item.rect.bottom) {
					selecetedItem = item;
				}
			}
		}
		
		return selecetedItem;
	}
	
	private Item findItemByUuid(String uuid) {
		for (Item i : mItems) {
			if (i.uuid.equals(uuid)) {
				return i;
			}
		}
		return null;
	}
	
	public void toggleDataInput() {
		isAccessingData = !isAccessingData;
	}
	
	public void toggleSyncing() {
		if (isPausedOutSync) {
			resumeSyncing();
		} else {
			pauseSyncing();
		}
	}
	
	public void resumeSyncing() {
		isPausedOutSync = false;
		
		for (JSONObject json : mWaitingList) {
			mJsonSender.onSend(json);
		}
		
		mWaitingList.clear();
	}
	
	public void pauseSyncing() {
		mWaitingList.clear();
		isPausedOutSync = true;
	}

	
	public void setDrawMode(int drawMode)
	{
		this.mCurrentMode = drawMode;
		switch (drawMode) {
		case DRAW_MODE_BRUSH:
			
			break;

		case DRAW_MODE_SHAPE:
			break;
		
		}
		invalidate();
		Log.e("ex", "setDrawMode");
	}


	@Override
	public void setTakenCropedBitmap(Bitmap bm) {
		Log.e("ex", "setTakenCropedBitmap");
		Rect shapeRect = shapeDrawer1.getDrawRect();
		Rect scaledRect = new Rect((int)(shapeRect.left/scaleX), (int)(shapeRect.top/scaleY), (int)((shapeRect.left+shapeRect.width())/scaleX), (int)((shapeRect.top+shapeRect.height())/scaleY));
		
		JSONObject json = new JSONObject();
		JSONObject params = new JSONObject();
		
		JSONArray rect = new JSONArray();
		rect.put(scaledRect.left);
		rect.put(scaledRect.top);
		rect.put(scaledRect.right);
		rect.put(scaledRect.bottom);
		
		try {
			json.put("uuid", uuid);
			json.put("action", "draw");
			params.put("type", "photo");

			params.put("dstRect", rect);
			
			String data = Util.toBase64(bm);
			
			System.out.println(data.length());
			
			params.put("bitmap", data);
			json.put("params", params);
			mJsonSender.onSend(json);
		} catch (JSONException e) {
			e.printStackTrace();
		}

		
		Rect srcRect = new Rect(0, 0, bm.getWidth(), bm.getHeight());
		mCanvas.drawBitmap(bm, srcRect, scaledRect, mPaint);
		mCurrentMode = DRAW_MODE_BRUSH;
		invalidate();
	}
	
	public Rect getShapeRect()
	{
		return shapeDrawer1.getDrawRect();
		
	}
	
	public void setScreenSize(int width,int height)
	{
		//shapeDrawer.setScreenSize(width, height);
		shapeDrawer1.setCircle(width, height);
	}

}
